/*
    jME Terra package. A Terrain Engine for the Java Monkey Engine.
    Copyright (C) 2005-2006,  Tijl Houtbeckers
    
    **** LICENSE: ****

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation,
	version 2.1.
	
	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

	**** CONTACT: ****

    I can be reached through the jmonkeyengine.com forums (user "llama"), 
    or the jmonkeyengine.com contact page:
    jmonkeyengine.com/index.php?option=com_contact&task=view&contact_id=5           

*/

/**
 * @author Tijl Houtbeckers
 */

package org.llama.jmex.terra;

import java.nio.FloatBuffer;

import com.jme.math.Vector3f;
import java.nio.IntBuffer;

/**
 * Gateway to managing terrains.
 *
 */
/**
 * @author poosoft
 *
 */
public class TerraManager extends HeightMapManager {

    private int blocksize, mapsize, halfblocksize;

    public int getMapSize() {
        return mapsize;
    }
    
    String name;    
    public String getName() {
    	return name;
    }

    public TerraManager(String name, int mapsize, int blocksize) {
        super(mapsize);
        this.name = name;
        this.mapsize = mapsize;
        this.blocksize = blocksize;
        this.halfblocksize = blocksize / 2;
    }

    public int getBlockSize() {
        return blocksize;
    }
    public static final String K_TD = "td";
    
        
    /**
     * Return the TerraData at the given map index. 
     * Will return null if there is nothing in the maps for this
     */
    public TerraData getTerraData(int x, int y) {
    	return (TerraData) maps.get(gkey(x, y, K_TD));
    }
    public void addTerraData(TerraData tb, int x, int y) {
    	maps.put(gkey(x, y, K_TD), tb);
    }
    public TerraData removeTerraData(int x, int y) {
        return (TerraData) maps.remove(gkey(x, y, K_TD));
    }
    
    public int findMap(int point) {
    	if (point < 0)
    		return ((point+1) / mapsize) - 1;
    	return point/mapsize;
    }
    
    public int findBlock(int point) {
    	if (point < 0)
    		return ((point-halfblocksize+1) / blocksize);
    	
    	return (point + halfblocksize)/blocksize;
    }
    
    /*
     * returns the given height value at the terrain coordinates x,y 
     */
    public int getHeightMapValue(int x, int y) {
    	int mapx = findMap(x);
    	int mapy = findMap(y);
        IntBuffer b = getMapWithCaching(mapx, mapy);
        return b.get((y-(mapy*mapsize))*mapsize+(x-(mapx*mapsize)));
    }
    
    /*
     * set the given height value at the terrain coordinates x,y 
     */
    public void setHeightMapValue(int x, int y, int heightValue) {
    	int mapx = findMap(x);
    	int mapy = findMap(y);
    	getMapWithCaching(mapx, mapy).put((y-(mapy*mapsize))*mapsize+(x-(mapx*mapsize)), heightValue);
    }
    
    
    public Vector3f getVector3f(int x, int y, int startx, int starty, Vector3f stepScale, Vector3f ret) {
    	if (ret == null)
    		ret = new Vector3f();
    	int h = getHeightMapValue(x, y); 
        int ex = (byte) ( 0x0ff & (h >> 16) );
        int ez = (byte) ( 0x0ff & (h >> 24) );
        h &= 0x0ffff;
        
        ret.x = (x-startx+(ex/127f)) * stepScale.x;
        ret.y =  h * stepScale.y;
        ret.z = (y-starty + (ez/127f)) * stepScale.z;
        return ret;
    }
    
    public void setVector3fInBuffer(FloatBuffer buf3f, int index, int x, int y, int startx, int starty, Vector3f stepScale) {
//    	int h = getPoint(x, y); 
//       
//        buf3f.put(index * 3, (x-startx) * stepScale.x);
//        buf3f.put((index * 3) + 1, h * stepScale.y);
//        buf3f.put((index * 3) + 2, (y-starty) * stepScale.z);
        
        int h = getHeightMapValue(x, y); 
        int ex = (byte) ( 0x0ff & (h >> 16) );
        int ez = (byte) ( 0x0ff & (h >> 24) );
        h &= 0x0ffff;
        buf3f.put(index * 3, (x-startx+(ex/127f)) * stepScale.x);
        buf3f.put((index * 3) + 1, h * stepScale.y);
        buf3f.put((index * 3) + 2, (y-starty + (ez/127f)) * stepScale.z);     
        
    }
    
    public int getBlockTopPosition(int block) {
		return (blocksize * block) - halfblocksize;
    }
    
    public int getBlockBottomPosition(int block) {
		return halfblocksize + (blocksize * block);
    }
    
    public int distanceToBlock(int block, int point) {
    	return Math.min(Math.abs(getBlockTopPosition(block) - point), Math.abs(getBlockBottomPosition(block) - point));
    }
    
    public int distanceToBlock(int blockx, int blocky, int x, int y) {
    	return hypot(distanceToBlock(blockx, x), distanceToBlock(blocky, y));
    }
    
    public int distanceToBlockSquared(int blockx, int blocky, int x, int y) {
    	int disx = distanceToBlock(blockx, x);
    	int disy = distanceToBlock(blocky, y);
    	return (disx * disx) + (disy * disy);
    }
    
    public int getMapTopPosition(int map) {
		return mapsize * map;
    }
    
    public int getMapBottomPosition(int map) {
		return mapsize * (map+1);
    }
    
    public int distanceToMap(int map, int point) {
    	return Math.min(Math.abs(getMapTopPosition(map) - point), Math.abs(getMapBottomPosition(map) - point));
    }
    
    public int distanceToMap(int mapx, int mapy, int x, int y) {
    	return hypot(distanceToMap(mapx, x), distanceToMap(mapy, y));
    }
    
    public long distanceToMapSquared(int mapx, int mapy, int x, int y) {
    	long disx = distanceToMap(mapx, x);
    	long disy = distanceToMap(mapy, y);
    	return (disx * disx) + (disy * disy);
    }
    
    public final static int hypot(int number1, int number2) {
//    	return isqrt((number1 * number1) + (number2 * number2));
    	return (int) Math.sqrt((number1 * number1) + (number2 * number2));
    }

//    public final static int isqrt(int number) {
//        int n  = 1;
//        int n1 = n + number / n >> 1; 
//
//        while(Math.abs(n1 - n) > 1) {
//          n  = n1;
//          n1 = n + number / n >> 1;
//        }
//        while((n1*n1) > number) {
//          n1 -= 1;
//        }
//        return n1;
//      }
    
    
//    public int block2Map(int k) {
//        boolean turn = false;
//        if (k < 0) {
//            k=-k;
//            turn=true;
//        }
//       
//        float nbs = (getMapSize() - 1)/ (getBlockSize() - 1);
//        int mapk = (int) Math.floor((k - (nbs / 2f)) / nbs) + 1;
//        if (turn) {
//            return -mapk;
//        }
//        else 
//            return mapk;
//    }
//    
//    public int block2Section(int k) {
//        boolean turn = false;
//        if (k < 0) {
//            k=-k;
//            turn=true;
//        }
//        int nbs = (getMapSize() - 1)/ (getBlockSize() - 1);
//        
//        float ek = (k - (nbs / 2f)) / nbs + 1;
//        
//        int seck= (int)Math.floor((ek - Math.floor(ek)) * nbs);
//        
//        if (turn) {
//            return nbs - 1 - seck;
//        }
//        else 
//            return seck;
//    }
  
}